using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;


namespace Qreed.Xna
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class AxisComponent : DrawableGameComponent
    {
        private bool _isDirty = true;
        private VertexBuffer _vertexBuffer;
        private BasicEffect _effect;
        private VertexDeclaration _vertexDeclaration;

        /// <summary>
        /// Initializes a new instance of the <see cref="AxisComponent"/> class.
        /// </summary>
        /// <param name="game">The Game that the game component should be attached to.</param>
        public AxisComponent(Game game, ICamera camera)
            : base(game)
        {
            _camera = camera;
        }

        private float _axisLength = 10.0f;
        /// <summary>
        /// Gets or sets the length of the axis.
        /// </summary>
        /// <value>The length of the axis.</value>
        public float AxisLength
        {
            get { return this._axisLength; }
            set 
            { 
                this._axisLength = value;
                _isDirty = true;
            }
        }

        private ICamera _camera;
        /// <summary>
        /// Gets or sets the camera.
        /// </summary>
        /// <value>The camera.</value>
        public ICamera Camera
        {
            get { return this._camera; }
            set { this._camera = value; }
        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            base.Initialize();
        }

        /// <summary>
        /// Called when graphics resources need to be loaded. Override this method to load any component-specific graphics resources.
        /// </summary>
        protected override void LoadContent()
        {
            Create3DAxis();

            _effect = new BasicEffect(GraphicsDevice, null);
            _effect.VertexColorEnabled = true;

            _vertexDeclaration = new VertexDeclaration(GraphicsDevice, VertexPositionColor.VertexElements);

            base.LoadContent();
        }

        /// <summary>
        /// Called when graphics resources need to be unloaded. Override this method to unload any component-specific graphics resources.
        /// </summary>
        protected override void UnloadContent()
        {
            if (_vertexDeclaration != null)
            {
                _vertexDeclaration.Dispose();
                _vertexDeclaration = null;
            }

            if (_vertexBuffer != null)
            {
                _vertexBuffer.Dispose();
                _vertexBuffer = null;
            }

            if (_effect != null)
            {
                _effect.Dispose();
                _effect = null;
            }

            base.UnloadContent();
        }

        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            if (Enabled && _isDirty)
                Create3DAxis();
            
            base.Update(gameTime);
        }

        private void Create3DAxis()
        {
            int vertexCount = 6;

            VertexPositionColor[] vertices = new VertexPositionColor[vertexCount];
            // X axis
            vertices[0] = new VertexPositionColor(new Vector3(0.0f, 0.0f, 0.0f), Color.Red);
            vertices[1] = new VertexPositionColor(new Vector3(AxisLength, 0.0f, 0.0f), Color.Red);
            // Y axis
            vertices[2] = new VertexPositionColor(new Vector3(0.0f, 0.0f, 0.0f), Color.Green);
            vertices[3] = new VertexPositionColor(new Vector3(0.0f, AxisLength, 0.0f), Color.Green);
            // Z axis
            vertices[4] = new VertexPositionColor(new Vector3(0.0f, 0.0f, 0.0f), Color.Blue);
            vertices[5] = new VertexPositionColor(new Vector3(0.0f, 0.0f, AxisLength), Color.Blue);
            
            _vertexBuffer = new VertexBuffer(GraphicsDevice, 
                                             vertexCount * VertexPositionColor.SizeInBytes,
                                             BufferUsage.WriteOnly);

            _vertexBuffer.SetData<VertexPositionColor>(vertices);

            _isDirty = false;
        }

        /// <summary>
        /// Called when the DrawableGameComponent needs to be drawn. Override this method with component-specific drawing code. Reference page contains links to related conceptual articles.
        /// </summary>
        /// <param name="gameTime">Time passed since the last call to Draw.</param>
        public override void Draw(GameTime gameTime)
        {
            if (Visible)
            {
                _effect.World = Matrix.Identity;
                _effect.View = Camera.View;
                _effect.Projection = Camera.Projection;

                GraphicsDevice.VertexDeclaration = _vertexDeclaration;
                GraphicsDevice.Vertices[0].SetSource(_vertexBuffer, 0, VertexPositionColor.SizeInBytes);

                _effect.Begin();
                foreach (EffectPass pass in _effect.CurrentTechnique.Passes)
                {
                    pass.Begin();
                    GraphicsDevice.DrawPrimitives(PrimitiveType.LineList, 0, 3);
                    pass.End();
                }
                _effect.End();
            }

            base.Draw(gameTime);
        }
    }
}